const express = require('express');
const fs = require('fs').promises;
const { exec } = require('child_process');
const path = require('path');

const app = express();
const CONFIG_PATH = '/etc/shutdown-schedules.json';
const CRON_PATH = '/etc/crontab';
const LOG_PATH = '/var/log/shutdown-manager.log';

app.use(express.json());

// 辅助函数
const validateTime = (time, type) => {
    if (type === 'daily') {
        return /^([01]\d|2[0-3]):[0-5]\d$/.test(time);
    }
    return !isNaN(new Date(time).getTime());
};

const log = async (message) => {
    const timestamp = new Date().toISOString();
    const logMessage = `[${timestamp}] ${message}\n`;
    try {
        await fs.appendFile(LOG_PATH, logMessage);
    } catch (err) {
        console.error('日志写入失败:', err.message);
    }
};

// 获取所有计划
app.get('/api/schedules', async (req, res) => {
    try {
        const data = await fs.readFile(CONFIG_PATH, 'utf8');
        res.json(JSON.parse(data));
    } catch (err) {
        if (err.code === 'ENOENT') return res.json([]);
        res.status(500).send(err.message);
    }
});

// 添加计划
app.post('/api/shutdown', async (req, res) => {
    const { type, time, delay, force } = req.body;

    try {
        // 验证输入
        if (!validateTime(time, type)) {
            await log(`添加计划失败: 无效的时间格式`);
            return res.status(400).send('无效的时间格式');
        }

        let newSchedule;
        if (type === 'daily') {
            // 处理每日计划
            const cronJob = `${time.split(':')[1]} ${time.split(':')[0]} * * * root /sbin/shutdown -h now\n`;

            // 检查是否已存在
            const cronContent = await fs.readFile(CRON_PATH, 'utf8');
            if (cronContent.includes(cronJob)) {
                await log(`添加计划失败: 每日计划 ${time} 已存在`);
                return res.status(409).send('该每日计划已存在');
            }

            // 写入 crontab
            await fs.appendFile(CRON_PATH, cronJob);

            newSchedule = {
                id: `cron_${Date.now()}`,
                type: 'daily',
                time,
                status: 'active',
                params: { force }
            };
        } else {
            // 处理单次计划
            const shutdownTime = new Date(time);
            const minutes = Math.round((shutdownTime - new Date()) / 60000);

            if (minutes < 1) {
                await log(`添加计划失败: 单次计划时间必须晚于当前时间`);
                return res.status(400).send('时间必须晚于当前时间');
            }

            await new Promise((resolve, reject) => {
                exec(`shutdown -h ${force ? '-f ' : ''}+${minutes}`, (err) => {
                    err ? reject(err) : resolve();
                });
            });

            newSchedule = {
                id: `once_${Date.now()}`,
                type: 'once',
                time: shutdownTime.toISOString(),
                status: 'active',
                params: { delay, force }
            };
        }

        // 更新配置文件
        const schedules = await fs.readFile(CONFIG_PATH, 'utf8')
            .catch(() => '[]')
            .then(JSON.parse);

        schedules.push(newSchedule);
        await fs.writeFile(CONFIG_PATH, JSON.stringify(schedules, null, 2));

        await log(`添加计划成功: ${JSON.stringify(newSchedule)}`);
        res.status(200).send('计划添加成功');
    } catch (err) {
        await log(`添加计划失败: ${err.message}`);
        res.status(500).send(err.message);
    }
});

// 删除计划
app.delete('/api/schedules/:id', async (req, res) => {
    const id = req.params.id;

    try {
        const schedules = await fs.readFile(CONFIG_PATH, 'utf8')
            .then(JSON.parse)
            .catch(() => []);

        const index = schedules.findIndex(s => s.id === id);
        if (index === -1) {
            await log(`删除计划失败: 计划 ${id} 不存在`);
            return res.status(404).send('计划不存在');
        }

        const [schedule] = schedules.splice(index, 1);

        // 删除系统计划
        if (schedule.type === 'daily') {
            await new Promise((resolve, reject) => {
                exec(`sed -i "/${schedule.time.split(':')[1]} ${schedule.time.split(':')[0]}/d
                " ${CRON_PATH}`, (err) => {
                    err ? reject(err) : resolve();
                });
            });
        } else {
            await new Promise((resolve, reject) => {
                exec('shutdown -c', (err) => { // 取消关机
                    err ? reject(err) : resolve();
                });
            });
        }

        await fs.writeFile(CONFIG_PATH, JSON.stringify(schedules, null, 2));
        await log(`删除计划成功: ${JSON.stringify(schedule)}`);
        res.sendStatus(200);
    } catch (err) {
        await log(`删除计划失败: ${err.message}`);
        res.status(500).send(err.message);
    }
});

// 定时更新计划状态
setInterval(async () => {
    try {
        const schedules = await fs.readFile(CONFIG_PATH, 'utf8')
            .then(JSON.parse)
            .catch(() => []);

        const now = new Date();
        let updated = false;

        schedules.forEach(schedule => {
            if (schedule.type === 'once' && schedule.status === 'active') {
                const scheduleTime = new Date(schedule.time);
                if (scheduleTime <= now) {
                    schedule.status = 'completed';
                    updated = true;
                }
            }
        });

        if (updated) {
            await fs.writeFile(CONFIG_PATH, JSON.stringify(schedules, null, 2));
            await log('计划状态更新成功');
        }
    } catch (err) {
        console.error('计划状态更新失败:', err.message);
    }
}, 60000); // 每分钟检查一次

// 静态文件服务
app.use(express.static(path.join(__dirname, 'public')));

app.listen(3000, () => {
    console.log('服务已启动: http://localhost:3000');
    // 初始化 crontab 和日志文件
    fs.access(CRON_PATH).catch(() => fs.writeFile(CRON_PATH, ''));
    fs.access(LOG_PATH).catch(() => fs.writeFile(LOG_PATH, ''));
});